package org.joshsacks.fishies.ui.gl;

import static javax.microedition.khronos.opengles.GL10.GL_DITHER;
import static javax.microedition.khronos.opengles.GL10.GL_TEXTURE_COORD_ARRAY;
import static javax.microedition.khronos.opengles.GL10.GL_VERTEX_ARRAY;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import org.joshsacks.fishies.FishiesController;
import org.joshsacks.fishies.GameBoard;
import org.joshsacks.fishies.GameBoard.MatchSet;
import org.joshsacks.fishies.GameBoard.SlideDownSet;
import org.joshsacks.fishies.GamePiece;
import org.joshsacks.fishies.R;

import android.content.Context;
import android.graphics.PointF;
import android.opengl.GLSurfaceView.Renderer;

public class FishiesRenderer implements Renderer {
  private final Context context;
  private PointF surfaceSize;
  private TextureManager textureManager = new TextureManager();
  private GameBoardRenderer renderer;
  private FishiesController controller;
  private int frameCount;
  private TouchIcon leftIcon;
  private TouchIcon rightIcon;
  private TouchIcon rotateIcon;
  private TouchIcon fallIcon;
  private MatchSetRenderer matchSetRenderer;
  private ScoreRenderer scoreRenderer;
  private final FontManager fontManager = new FontManager();
  private int reservedSpaceAtBottom;
  
  private long frameCountTime;
  
  public FishiesRenderer(Context context, FishiesController controller) {
    this.context = context;
    this.controller = controller;
    surfaceSize = new PointF();
  }

  public void onSurfaceCreated(GL10 gl, EGLConfig config) {   
    gl.glEnable(GL10.GL_TEXTURE_2D);      //Enable Texture Mapping ( NEW )
    gl.glShadeModel(GL10.GL_SMOOTH);      //Enable Smooth Shading
    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);  //Black Background
    gl.glClearDepthf(1.0f);           //Depth Buffer Setup
    gl.glEnable(GL10.GL_DEPTH_TEST);      //Enables Depth Testing
    gl.glDepthFunc(GL10.GL_LEQUAL);       //The Type Of Depth Testing To Do
    
    //Really Nice Perspective Calculations
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 
  }

  public void onSurfaceChanged(GL10 gl, int newWidth, int newHeight) {
    GameBoardRenderer.calculateSizeIndex(newWidth, newHeight);

    gl = TextureManager.debugWrapGl(gl);
    surfaceSize.set(newWidth, newHeight);
    renderer = new GameBoardRenderer(controller.getGameBoard(), textureManager);
    scoreRenderer = new ScoreRenderer(controller, textureManager, fontManager, surfaceSize, gl);
    reservedSpaceAtBottom = TouchIcon.ICON_SIZE + scoreRenderer.getHeight() + 
        GameBoardRenderer.getYOffset();
    
    gl.glDisable(GL_DITHER);
 
    gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); 
    gl.glEnable(GL10.GL_BLEND); 

    gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    gl.glShadeModel(GL10.GL_SMOOTH);
    gl.glEnableClientState(GL_VERTEX_ARRAY);
    
    gl.glViewport(0, 0, (int) surfaceSize.x, (int) surfaceSize.y);

    // Set our field of view.
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glFrustumf(
            -surfaceSize.x / 2, surfaceSize.x / 2, 
            -surfaceSize.y / 2, surfaceSize.y / 2,
            1, 3);

    // Position the camera at (0, 0, -2) looking down the -z axis.
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();
    // Points rendered to z=0 will be exactly at the frustum's 
    // (farZ - nearZ) / 2 so the actual dimension of the triangle should be
    // half
    gl.glTranslatef(-surfaceSize.x / 2, -surfaceSize.y / 2, -1);
    
    textureManager.init(context, gl); 
    fontManager.init(gl);
    int[] textureId = new int[2];
    gl.glGenTextures(2, textureId, 0);
    TextureManager.bindTexture(gl, context, textureId[0], R.drawable.arrrow);
    TextureManager.bindTexture(gl, context, textureId[1], R.drawable.rotate_arrow);
    leftIcon = new TouchIcon(TouchIcon.IconType.LEFT_ARROW, textureId[0], 0);
    rightIcon = 
      new TouchIcon(TouchIcon.IconType.RIGHT_ARROW, textureId[0], 
              (int) surfaceSize.x - TouchIcon.ICON_SIZE);
    rotateIcon = 
      new TouchIcon(TouchIcon.IconType.LEFT_ARROW, textureId[1],
              (int) (surfaceSize.x / 4));
    fallIcon = 
      new TouchIcon(TouchIcon.IconType.DOWN_ARROW, textureId[0],
              (int) (surfaceSize.x * 3 / 4) - TouchIcon.ICON_SIZE);
  }
  
  public void onDrawFrame(GL10 gl) {
    if (frameCount == 0) {
      frameCountTime = System.currentTimeMillis();
    }
    frameCount++;
   
    synchronized (controller.getSync()) {
      gl.glEnableClientState(GL_VERTEX_ARRAY);
      gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      gl.glPushMatrix();
      gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
      gl.glFrontFace(GL10.GL_CW);  

      gl.glPushMatrix();
      float midX = (surfaceSize.x - GameBoardRenderer.getTileSize() * GameBoard.WIDTH) / 2;
      gl.glTranslatef(midX, reservedSpaceAtBottom, 0);

      renderer.render(gl);
      renderGamePiece(gl);

      renderMatchSet(gl);
      gl.glPopMatrix();
      
      scoreRenderer.render(context, gl);
      renderDirectionArrows(gl);

      gl.glDisableClientState(GL_VERTEX_ARRAY);
      gl.glDisableClientState(GL_TEXTURE_COORD_ARRAY);

      gl.glPopMatrix();
    }
    if (System.currentTimeMillis() - frameCountTime > 5000) {
      System.err.println("FPS: " + 
              frameCount * 1000.0 / (System.currentTimeMillis() - frameCountTime));
      frameCount = 0;
    }
    controller.setRendeded();
  }

  private void renderMatchSet(GL10 gl) {
    if (matchSetRenderer != null) {
      matchSetRenderer.render(context, gl);
    }    
  }

  private void renderDirectionArrows(GL10 gl) {
    leftIcon.render(gl);
    rightIcon.render(gl);
    rotateIcon.render(gl);
    fallIcon.render(gl);
  }

  private void renderGamePiece(GL10 gl) {
   GamePiece gamePiece = controller.getCurrentPiece();
   if (gamePiece == null) {
     return;
   }

   int x = (int) gamePiece.getX();
   double y = gamePiece.getY();
 
   for (int i = 0; i< GamePiece.PIECE_SIZE; i++) {
     SquareRenderer renderer = new SquareRenderer(gl);
     textureManager.setTexture(gl, gamePiece.typeAtPosition(i).ordinal());
     renderer.addSquare(x, (int) (y - (i + 1) * GameBoardRenderer.getTileSize()), 
             GameBoardRenderer.getTileSize(), false);
     renderer.draw();
   }
  }
  
  public void animateArrow(boolean left) {
    if (left) {
      leftIcon.animate();
    } else {
      rightIcon.animate();
    }
  }
 
  public void animateRotate() {
    rotateIcon.animate();
  }
  
  public void setMatchSet(MatchSet matchSet) {
    if (matchSetRenderer != null) {
      matchSetRenderer.discard();
    }
    
    if (matchSet != null) {
      this.matchSetRenderer =
        new MatchSetRenderer(controller, matchSet, textureManager, fontManager);
    } else {
      this.matchSetRenderer = null;
    }
  }

  public void setSlideDownSet(SlideDownSet slideDownSet) {
    renderer.setSlideDownSet(slideDownSet);    
  }

  public void highlightDownButton(boolean highlight) {
    fallIcon.setPressed(highlight);
  }
}
